/**
 * @author Vitor Franco do Carmo (vitor.carmo@cpmbraxis.com)
 * @since 28/07/2013
 *
 */
package br.com.vfcfw.commons.domain.entity;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javax.persistence.MappedSuperclass;

import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.DynamicUpdate;

import br.com.vfcfw.commons.util.ReflexaoUtil;

/**
 * Classe base para todas as entidades persistentes.
 * 
 * @author Vitor Franco do Carmo (vitor.carmo@cpmbraxis.com)
 * @since 28/07/2013
 * 
 */
@MappedSuperclass
@DynamicUpdate(true)
public abstract class BaseEntity implements Serializable {

    /** */
    private static final long serialVersionUID = 2253010616095226024L;

    /**
     * Construtor da classe.
     */
    public BaseEntity() {

        super();
    }

    /**
     * Retorna o valor do atributo que representa o indentificador da entidade persistente. <p><b>ATENÇÃO!!!</b></p>
     * Deve ser implementado por todas as entitdades persistentes apontando para o atributo que possui a anotação @Id.
     * 
     * @return Valor do identificador da entidade persistente.
     */
    public abstract Serializable getChavePrimaria();

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {

        final int prime = 31;
        int result = 1;
        result = prime * result + ((getChavePrimaria() == null) ? 0 : getChavePrimaria().hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {

            if (!(obj instanceof BaseEntity)) {
                return false;
            }
            if ((getClass() != obj.getClass().getSuperclass() && (getClass().getSuperclass() != obj.getClass()))) {
                return false;
            } else {
                return isBaseEntityIguais(obj);
            }
        } else {
            return isBaseEntityIguais(obj);
        }
    }

    /**
     * Verifica se o objeto é igual ao objeto informado.
     * 
     * @param obj Objeto que será comparado ao objeto atual.
     * @return <tt>TRUE</tt> se forem iguais, <tt>FALSE</tt> caso contrário.
     */
    private boolean isBaseEntityIguais(Object obj) {

        BaseEntity outroObjeto = (BaseEntity) obj;
        if (getChavePrimaria() == null) {
            return super.equals(outroObjeto);
        } else {
            if (outroObjeto.getChavePrimaria() == null) {
                return false;
            } else {
                return getChavePrimaria().equals(outroObjeto.getChavePrimaria());
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {

        String strEntity = null;
        StringBuffer stringBuffer = new StringBuffer();

        Class<?> classe = this.getClass();
        Field[] atributos = classe.getDeclaredFields();

        stringBuffer.append(classe.getName()).append(" {\n");

        for (int i = 0; i < atributos.length; i++) {
            if (!"serialVersionUID".equals(atributos[i].getName())) {

                stringBuffer.append("\t").append(atributos[i].getType().getName()).append(" ")
                    .append(atributos[i].getName()).append(" = ");
                try {
                    Method metodo = classe.getDeclaredMethod(String.format("get%s",
                        StringUtils.capitalize(atributos[i].getName())));
                    stringBuffer.append(ReflexaoUtil.executarMetodo(metodo, this));
                } catch (Exception e) {
                }
                stringBuffer.append(";\n").append((i + 1 < atributos.length) ? "" : "}");
            }
        }

        strEntity = stringBuffer.toString();

        stringBuffer = null;

        return strEntity;
    }

}
